// The MIT License (MIT)

// Typed.js | Copyright (c) 2014 Matt Boldt | www.mattboldt.com

// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:

// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.

// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

! function($) {

    "use strict";

    var Typed = function(el, options) {

        // выбранный элемент для управления текстом
        this.el = $(el);

        // опции
        this.options = $.extend({}, $.fn.typed.defaults, options);

        // атрибут для ввода
        this.isInput = this.el.is('input');
        this.attr = this.options.attr;

        // показать курсор
        this.showCursor = this.isInput ? false : this.options.showCursor;

        // текстовое содержимое элемента
        this.elContent = this.attr ? this.el.attr(this.attr) : this.el.text()

        // html или обычный текст
        this.contentType = this.options.contentType;

        // скорость набора текста
        this.typeSpeed = this.options.typeSpeed;

        // добавить задержку перед началом набора текста
        this.startDelay = this.options.startDelay;

        // скорость обратного хода
        this.backSpeed = this.options.backSpeed;

        // количество времени ожидания перед возвратом
        this.backDelay = this.options.backDelay;

        // ввод строк текста
        this.strings = this.options.strings;

        // позиция номера символа текущей строки
        this.strPos = 0;

        // текущая позиция массива
        this.arrayPos = 0;

        // число, на котором нужно остановить возврат. по умолчанию 0, 
        // может меняться в зависимости от того, сколько символов вы 
        // хотите удалить за один раз
        this.stopNum = 0;

        // Циклическая логика
        this.loop = this.options.loop;
        this.loopCount = this.options.loopCount;
        this.curLoop = 0;

        // для остановки
        this.stop = false;

        // пользовательский курсор
        this.cursorChar = this.options.cursorChar;

        // Все системы работают!
        this.build();
    };

    Typed.prototype = {

        constructor: Typed

        ,
        init: function() {
            // начать цикл с первой текущей строки (глобальной self.string) 
            // текущая строка будет передаваться в качестве аргумента 
            // каждый раз после этого
            var self = this;
            self.timeout = setTimeout(function() {
                // Начните печатать
                self.typewrite(self.strings[self.arrayPos], self.strPos);
            }, self.startDelay);
        }

        ,
        build: function() {
            // Вставить курсор
            if (this.showCursor === true) {
                this.cursor = $("<span class=\"typed-cursor\">" + this.cursorChar + "</span>");
                this.el.after(this.cursor);
            }
            this.init();
        }

        // передавать текущее состояние строки каждой функции, вводит 1 символ за вызов
        ,
        typewrite: function(curString, curStrPos) {
            // выход при остановке
            if (this.stop === true) {
                return;
            }

            // различные значения для setTimeout во время ввода не могут быть глобальными, 
            // поскольку число изменяется каждый раз, когда выполняется цикл.
            var humanize = Math.round(Math.random() * (100 - 30)) + this.typeSpeed;
            var self = this;

            // ------------- oнеобязательный ------------- //
            // возвращает определенную строку быстрее
            // ------------------------------------------- //
            // if (self.arrayPos == 1){
            //  self.backDelay = 50;
            // }
            // else{ self.backDelay = 500; }

            // содержать функцию ввода в тайм-аут, гуманизированную задержку
            self.timeout = setTimeout(function() {
                // проверить escape-символ перед значением паузы
                // формат: \^\d+ .. например: ^1000 ..  \ ^ \ d + .. также должен иметь возможность печатать ^ с помощью ^^
                // одиночные ^ удаляются из строки
                var charPause = 0;
                var substr = curString.substr(curStrPos);
                if (substr.charAt(0) === '^') {
                    var skip = 1; // skip atleast 1
                    if (/^\^\d+/.test(substr)) {
                        substr = /\d+/.exec(substr)[0];
                        skip += substr.length;
                        charPause = parseInt(substr);
                    }

                    // удалить escape-символ и значение паузы, чтобы они не печатались
                    curString = curString.substring(0, curStrPos) + curString.substring(curStrPos + skip);
                }

                if (self.contentType === 'html') {
                    // пропускать html-теги при наборе текста
                    var curChar = curString.substr(curStrPos).charAt(0)
                    if (curChar === '<' || curChar === '&') {
                        var tag = '';
                        var endTag = '';
                        if (curChar === '<') {
                            endTag = '>'
                        } else {
                            endTag = ';'
                        }
                        while (curString.substr(curStrPos).charAt(0) !== endTag) {
                            tag += curString.substr(curStrPos).charAt(0);
                            curStrPos++;
                        }
                        curStrPos++;
                        tag += endTag;
                    }
                }

                // тайм-аут для любой паузы после символа
                self.timeout = setTimeout(function() {
                    if (curStrPos === curString.length) {
                        // запускает функцию обратного вызова
                        self.options.onStringTyped(self.arrayPos);

                        // это последняя строка
                        if (self.arrayPos === self.strings.length - 1) {
                            // анимация, которая возникает на последней набранной строке
                            self.options.callback();

                            self.curLoop++;

                            // выйти, если мы не будем возвращаться назад
                            if (self.loop === false || self.curLoop === self.loopCount)
                                return;
                        }

                        self.timeout = setTimeout(function() {
                            self.backspace(curString, curStrPos);
                        }, self.backDelay);
                    } else {

                        /* вызов перед функциями, если применимо */
                        if (curStrPos === 0)
                            self.options.preStringTyped(self.arrayPos);

                        // начните вводить каждый новый символ в существующую строку 
                        // curString: arg, self.el.html: исходный текст внутри элемента
                        var nextString = curString.substr(0, curStrPos + 1);
                        if (self.attr) {
                            self.el.attr(self.attr, nextString);
                        } else {
                            if (self.isInput) {
                                self.el.val(nextString);
                            } else if (self.contentType === 'html') {
                                self.el.html(nextString);
                            } else {
                                self.el.text(nextString);
                            }
                        }

                        // добавлять символы по одному
                        curStrPos++;
                        // зациклить функцию
                        self.typewrite(curString, curStrPos);
                    }
                    // пауза в конце символа
                }, charPause);

                // гуманизированное значение для ввода
            }, humanize);

        }

        ,
        backspace: function(curString, curStrPos) {
            // выход при остановке
            if (this.stop === true) {
                return;
            }

            // различные значения для setTimeout во время набора текста
            // не может быть глобальным, поскольку число изменяется при каждом выполнении цикла
            var humanize = Math.round(Math.random() * (100 - 30)) + this.backSpeed;
            var self = this;

            self.timeout = setTimeout(function() {

                // ----- эта часть не является обязательной ----- //
                // проверьте позицию строкового массива в первой строке, 
                // удалите только одно слово, stopNum фактически представляет 
                // количество символов, которые нужно сохранить в текущей строке. 
                // В моем случае это 14..
                // if (self.arrayPos == 1){
                //  self.stopNum = 14;
                // }
                // через раз удалять всю набранную строку
                // else{
                //  self.stopNum = 0;
                // }

                if (self.contentType === 'html') {
                    // пропускать html-теги при возврате
                    if (curString.substr(curStrPos).charAt(0) === '>') {
                        var tag = '';
                        while (curString.substr(curStrPos).charAt(0) !== '<') {
                            tag -= curString.substr(curStrPos).charAt(0);
                            curStrPos--;
                        }
                        curStrPos--;
                        tag += '<';
                    }
                }

                // ----- продолжить важные дела ----- //
                // заменить текст базовым текстом + печатные символы
                var nextString = curString.substr(0, curStrPos);
                if (self.attr) {
                    self.el.attr(self.attr, nextString);
                } else {
                    if (self.isInput) {
                        self.el.val(nextString);
                    } else if (self.contentType === 'html') {
                        self.el.html(nextString);
                    } else {
                        self.el.text(nextString);
                    }
                }

                // если число (идентификатор символа в текущей строке) 
                // меньше, чем номер остановки, продолжайте
                if (curStrPos > self.stopNum) {
                    // вычитать символы один за другим
                    curStrPos--;
                    // зациклить функцию
                    self.backspace(curString, curStrPos);
                }
                // если достигнут номер остановки, увеличить 
                // позицию массива до следующей строки
                else if (curStrPos <= self.stopNum) {
                    self.arrayPos++;

                    if (self.arrayPos === self.strings.length) {
                        self.arrayPos = 0;
                        self.init();
                    } else
                        self.typewrite(self.strings[self.arrayPos], curStrPos);
                }

                // гуманизированное значение для ввода
            }, humanize);

        }

        // Start & Stop в настоящее время не работает

        // , stop: function() {
        //     var self = this;

        //     self.stop = true;
        //     clearInterval(self.timeout);
        // }

        // , start: function() {
        //     var self = this;
        //     if(self.stop === false)
        //        return;

        //     this.stop = false;
        //     this.init();
        // }

        // Сбросить и перестроить элемент
        ,
        reset: function() {
            var self = this;
            clearInterval(self.timeout);
            var id = this.el.attr('id');
            this.el.after('<span id="' + id + '"/>')
            this.el.remove();
            if (typeof this.cursor !== 'undefined') {
                this.cursor.remove();
            }
            // Отправить обратный вызов
            self.options.resetCallback();
        }

    };

    $.fn.typed = function(option) {
        return this.each(function() {
            var $this = $(this),
                data = $this.data('typed'),
                options = typeof option == 'object' && option;
            if (!data) $this.data('typed', (data = new Typed(this, options)));
            if (typeof option == 'string') data[option]();
        });
    };

    $.fn.typed.defaults = {
        strings: ["These are the default values...", "You know what you should do?", "Use your own!", "Have a great day!"],
        // скорость набора текста
        typeSpeed: 0,
        // время до начала набора текста
        startDelay: 0,
        // скорость обратного хода
        backSpeed: 0,
        // время до возврата
        backDelay: 500,
        // loop петля
        loop: false,
        // false = infinite(бесконечный)
        loopCount: false,
        // показать курсор
        showCursor: true,
        // символ для курсора
        cursorChar: "|",
        // атрибут для типа (null == text)
        attr: null,
        // либо html, либо текст
        contentType: 'html',
        // вызов после выполнения функции обратного вызова
        callback: function() {},
        // запуск функции обратного вызова перед каждой строкой
        preStringTyped: function() {},
        // обратный вызов для каждой введенной строки
        onStringTyped: function() {},
        // обратный вызов для сброса
        resetCallback: function() {}
    };


}(window.jQuery);
